<?php

namespace App\Libraries\RDBS;

use \Phalcon\Di, \Phalcon\Db;

/**
 * 数据库操作类
 * @package App\Libraries\RDBS
 */
class Database {

    #region 属性

    /**
     * 依赖注册组件
     * @var \Phalcon\Di
     */
    protected $di;

    /**
     * \Phalcon\Db\Adapter\Pdo 对象
     * @var \Phalcon\Db\Adapter\Pdo
     */
    protected static $connection;

    #endregion

    #region 构造函数和初始化

    /**
     * 构造函数
     * @param $di \Phalcon\Di 对象
     */
    public function __construct($di = nulll) {
        $this->di = isset($di) ? $di : Di::getDefault();
    }

    #endregion

    #region 执行SQL语句

    /**
     * 执行SQL语句，返回查询的结果
     *
     * @param string $sqlStatement SQL语句
     * @param mixed $bindParams 绑定的参数
     * @param mixed $bindTypes 绑定的类型
     * @return bool|\Phalcon\Db\ResultInterface
     */
    public function query($sqlStatement, $bindParams = null, $bindTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection($this->_isDql($sqlStatement));
        }
        $result = self::$connection->query($sqlStatement, $bindParams, $bindTypes);
        $result->setFetchMode(\Phalcon\Db::FETCH_ASSOC);
        return $result;
    }

    /**
     * 执行SQL语句，返回影响的结果
     *
     * @param string $sqlStatement SQL语句
     * @param mixed $bindParams 绑定的参数
     * @param mixed $bindTypes 绑定的类型
     * @return bool
     */
    public function execute($sqlStatement, $bindParams = null, $bindTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection($this->_isDql($sqlStatement));
        }
        return self::$connection->execute($sqlStatement, $bindParams, $bindTypes);
    }

    #endregion

    #region 查询相关

    /**
     * 返回查询结果第一行的数据
     *
     * @param string $sqlQuery 查询SQL语句
     * @param mixed $bindParams 绑定的参数
     * @param mixed $bindTypes 绑定的类型
     * @return array
     */
    public function fetchOne($sqlQuery, $bindParams = null, $bindTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection(true);
        }
        $data = self::$connection->fetchOne($sqlQuery, Db::FETCH_ASSOC, $bindParams, $bindTypes);
        return $data ? $data : null;
    }

    /**
     *
     * 返回查询结果的所有数据
     *
     * @param string $sqlQuery 查询SQL语句
     * @param mixed $bindParams 绑定的参数
     * @param mixed $bindTypes 绑定的类型
     * @return array
     */
    public function fetchAll($sqlQuery, $bindParams = null, $bindTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection(true);
        }
        $list = self::$connection->fetchAll($sqlQuery, Db::FETCH_ASSOC, $bindParams, $bindTypes);
        return $list ? $list : array();
    }

    /**
     * 返回查询结果的第一行第N个字段数据
     *
     * @param string $sqlQuery 查询SQL语句
     * @param array $placeholders 占位符
     * @param int|string $column 字段
     * @return string|
     */
    public function fetchColumn($sqlQuery, $placeholders = null, $column = 0) {
        if (!isset(self::$connection)) {
            $this->_initConnection(true);
        }
        $value = self::$connection->fetchColumn($sqlQuery, $placeholders, $column);
        return $value ? $value : null;
    }

    #endregion

    #region 插入、修改、删除


    /**
     * 插入数据到数据库中 (插入全表数据时使用)
     *
     * @param string|array $table 要插入数据的表
     * @param array $values 要插入的值
     * @param mixed $fields 对应的字段名
     * @param mixed $dataTypes 数据类型
     * @return boolean
     */
    public function insert($table, array $values, $fields = null, $dataTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection(false);
        }
        return self::$connection->insert($table, $values, $fields, $dataTypes);
    }

    /**
     * 插入数据到数据库中
     * <code>
     *
     * @param  string table 要插入数据的表
     * @param array $data 要插入的键值对集合
     * @param  array dataTypes 数据类型
     * @return boolean
     */
    public function insertAsDict($table, $data, $dataTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection(false);
        }
        return self::$connection->insertAsDict($table, $data, $dataTypes);
    }

    /**
     * 返回插入id
     * @param string $sequenceName 序列的名称
     * @return int|boolean
     */
    public function lastInsertId($sequenceName = null) {
        return self::$connection->lastInsertId($sequenceName);
    }

    /**
     * 更新数据
     *
     * @param string|array $table 要更新数据的表
     * @param mixed $fields 字段
     * @param mixed $values 要更新的值
     * @param string |array $whereCondition 查询条件
     * @param array $dataTypes 数据类型
     * @return boolean
     */
    public function update($table, $fields, $values, $whereCondition = null, $dataTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection(false);
        }
        return self::$connection->update($table, $fields, $values, $whereCondition, $dataTypes);
    }

    /**
     * 更新数据
     *
     * @param string|array $table 要更新数据的表
     * @param mixed $data 要更新的值对集合
     * @param string |array $whereCondition 查询条件
     * @param array $dataTypes 数据类型
     *
     * @return boolean
     */
    public function updateAsDict($table, $data, $whereCondition = null, $dataTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection(false);
        }
        return self::$connection->updateAsDict($table, $data, $whereCondition, $dataTypes);
    }

    /**
     * 删除数据
     *
     * @param string|array $table 要更新数据的表
     * @param string $whereCondition 查询条件
     * @param array $placeholders 占位符
     * @param array $dataTypes 数据类型
     * @return boolean
     */
    public function delete($table, $whereCondition = null, $placeholders = null, $dataTypes = null) {
        if (!isset(self::$connection)) {
            $this->_initConnection(false);
        }
        return self::$connection->delete($table, $whereCondition, $placeholders, $dataTypes);
    }

    /**
     * 返回 INSERT/UPDATE/DELETE 操作最后影响的行数
     *
     * @return int
     */
    public function affectedRows() {
        return self::$connection->affectedRows();
    }

    /**
     * 获取没有绑定参数的sql语句
     * @return string
     */
    function getRealSQLStatement() {
        return self::$connection->getRealSQLStatement();
    }

    /**
     * Active SQL statement in the object
     *
     * @return string
     */
    public function getSQLStatement() {
        return self::$connection->getSQLStatement();
    }

    #endregion

    #region 事务

    /**
     * 开始事务
     *
     * @param bool $nesting 是否嵌套
     * @return bool
     */
    public function begin($nesting = true) {
        $this->_initConnection(false);
        self::$connection->begin($nesting);
    }

    /**
     * 回滚事务
     *
     * @param bool $nesting 是否嵌套
     * @return bool
     */
    public function rollback($nesting = true) {
        self::$connection->rollback($nesting);
    }

    /**
     * 提交事务
     *
     * @param bool $nesting 是否嵌套
     * @return bool
     */
    public function commit($nesting = true) {
        self::$connection->commit($nesting);
    }

    /**
     * 返回错误信息
     * @return array
     */
    public function getErrorInfo() {
        $error = self::$connection->getErrorInfo();
        if (!$error || $error[0] === "00000")
            return false;
        return $error;
    }

    #endregion

    #region 私有方法

    /**
     * SQL语句是数据查询语言DQL
     * @param $sqlStatement SQL语句
     * @return boolean
     */
    private function _isDql($sqlStatement) {
        $sqlStatement = trim($sqlStatement);
        return strtoupper(substr($sqlStatement, 0, 6)) == "SELECT";
    }

    /**
     * 初始化\Phalcon\Db\Adapter\Pdo 对象
     * @param bool $read 是否只读
     */
    private function _initConnection($read = true) {
        $name = $read ? "dbSlave1" : "dbMaster";
        self::$connection = $this->di->getShared($name);
    }

    #endregion
}